package indexer

import (
	"archive/tar"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/opencloudos/onyx/pkg/types"
)

func BuildLayerIndex(layerPath string, blockSize int) (*types.LayerIndex, error) {
	f, err := os.Open(layerPath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	var reader io.Reader = f
	if strings.HasSuffix(layerPath, ".gz") {
		gzr, err := gzip.NewReader(f)
		if err != nil {
			return nil, err
		}
		defer gzr.Close()
		reader = gzr
	}

	tr := tar.NewReader(reader)
	layerIndex := &types.LayerIndex{Files: []types.FileEntry{}}

	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, err
		}
		if hdr.FileInfo().IsDir() {
			continue
		}

		fileEntry, err := processFile(tr, hdr, blockSize)
		if err != nil {
			return nil, fmt.Errorf("process %s: %v", hdr.Name, err)
		}
		layerIndex.Files = append(layerIndex.Files, *fileEntry)
	}
	return layerIndex, nil
}

func processFile(r io.Reader, hdr *tar.Header, blockSize int) (*types.FileEntry, error) {
	var blocks []types.FileBlock
	h := sha256.New()
	buf := make([]byte, blockSize)
	var offset int64

	for {
		n, err := io.ReadFull(r, buf)
		if err != nil && err != io.ErrUnexpectedEOF && err != io.EOF {
			return nil, err
		}
		if n == 0 {
			break
		}

		blockHash := sha256.Sum256(buf[:n])
		block := types.FileBlock{
			Offset: offset,
			Size:   int64(n),
			Hash:   hex.EncodeToString(blockHash[:]),
		}
		blocks = append(blocks, block)
		h.Write(buf[:n])
		offset += int64(n)

		if err == io.EOF || err == io.ErrUnexpectedEOF {
			break
		}
	}

	fileHash := hex.EncodeToString(h.Sum(nil))
	entry := &types.FileEntry{
		Path:     hdr.Name,
		Size:     hdr.Size,
		FileHash: fileHash,
		Blocks:   blocks,
	}
	return entry, nil
}

func SaveIndex(idx *types.LayerIndex, outPath string) error {
	f, err := os.Create(outPath)
	if err != nil {
		return err
	}
	defer f.Close()

	return WriteJSON(f, idx)
}
